﻿using System.Collections.Generic;
using System.Linq;
using CID.Domain.Exceptions;
using NailsFramework.Persistence;

namespace CID.Domain.Operations
{
    public class Server : Model<Server>, IServer
    {
        public virtual int Id { get; private set; }
        public virtual string Name { get; set; }
        public virtual string Address { get; set; }
        public virtual bool Enabled { get; set; }
        public virtual ILocation Location { get; set; }

        private IList<IServerAssignment> assignments = new List<IServerAssignment>();
        public virtual IEnumerable<IServerAssignment> Assignments { get { return assignments; } }

        public virtual IEnumerable<IEnvironment> Environments 
        {
            get { return (from IServerAssignment assignment in Assignments select assignment.Environment).Distinct(); }
        }

        public virtual IEnumerable<IServerRole> Roles
        {
            get { return (from IServerAssignment assignment in Assignments select assignment.ServerRole).Distinct(); }
        }

        protected Server()
        {
            Enabled = true;
        }

        public Server(string name, string address, ILocation location) : this()
        {
            Name = name;
            Address = address;
            Location = location;
        }

        #region Equals and GetHashCode
        //This class needs to have the Equals overriden to support the comparison with the proxies from the persistence layer
        public override bool Equals(object obj)
        {
            return EntityEqualityHelper.Equals(this, obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        #endregion

        public virtual void Assign(IEnvironment environment, IServerRole serverRole)
        {
            var assignment = new ServerAssignment(this, serverRole, environment);
            AddAssignment(assignment);
            environment.AddAssignment(assignment);
            serverRole.AddAssignment(assignment);
        }

        public virtual void Unassign(IEnvironment environment, IServerRole serverRole)
        {
            var assignment = Assignments.FirstOrDefault(x => (x.Environment.Equals(environment)) && (x.ServerRole.Equals(serverRole)));
            if (assignment != null)
            {
                RemoveAssignment(assignment);
                environment.RemoveAssignment(assignment);
                serverRole.RemoveAssignment(assignment);
            }
        }

        public virtual void AddAssignment(IServerAssignment assignment)
        {
            if (!Equals(assignment.Server)) throw new InvalidAssignmentException(this);

            if (Assignments.Count(x => (x.Environment.Equals(assignment.Environment)) && (x.ServerRole.Equals(assignment.ServerRole))) > 0)
                throw new DuplicateAssignmentException(this, assignment.Environment, assignment.ServerRole);

            assignments.Add(assignment);
        }

        public virtual void RemoveAssignment(IServerAssignment assignment)
        {
            if (!Equals(assignment.Server)) throw new InvalidAssignmentException(this);

            if (assignments.Contains(assignment)) assignments.Remove(assignment);

        }
    }
}
